/*
 * Copyright (C) 2010 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.zxing.decoding;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.ResultPoint;
import com.google.zxing.answercard.AnswerCardReader;
import com.google.zxing.common.HybridBinarizer;

import com.ciwong.libs.utils.CWLog;
import com.ciwong.mobilelib.R;
import com.zxing.camera.CameraManager;
import com.zxing.camera.PlanarYUVLuminanceSource;
import com.zxing.i.ZXingInterface;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.util.Hashtable;

public final class DecodeHandler extends Handler {

    private static final String TAG = DecodeHandler.class.getSimpleName();

    private final Activity activity;

    private final MultiFormatReader multiFormatReader;

    private ZXingInterface zingInterface;

    private AnswerCardCheckUtil mAnswerCardCheckUtil = new AnswerCardCheckUtil();

    DecodeHandler(Activity activity, Hashtable<DecodeHintType, Object> hints,
                  ZXingInterface inter) {
        multiFormatReader = new MultiFormatReader();
        multiFormatReader.setHints(hints);
        this.activity = activity;
        this.zingInterface = inter;
    }

    @Override
    public void handleMessage(Message message) {

        if (message.what == R.id.decode) {
            // Log.d(TAG, "Got decode message");
            decode((byte[]) message.obj, message.arg1, message.arg2);
        } else if (message.what == R.id.quit) {
            Looper.myLooper().quit();
        }
    }

    /**
     * Decode the data within the viewfinder rectangle, and time how long it
     * took. For efficiency, reuse the same reader objects from one decode to
     * the next.
     *
     * @param data   The YUV preview frame.
     * @param width  The width of the preview frame.
     * @param height The height of the preview frame.
     */
//    boolean flage=true;
    int flage=0;

    private void decode(byte[] data, int width, int height) {
        if (flage<2) {
            Message message = Message.obtain(zingInterface.getHandler(), R.id.decode_failed);
            message.sendToTarget();
            Log.d(TAG, "DecodeHandler decode flage进来了"+flage);
            ++flage;
            return;
        }
        if (!CameraManager.get().isNeedUseZxingScan()) {
            Message message = Message.obtain(zingInterface.getHandler(),
                    R.id.decode_failed);
            message.sendToTarget();
            return;
        }

        if (data == null) {
            CWLog.d("decode", "data == null");
            multiFormatReader.reset();
            return;
        }
         CWLog.d("decode", "width = " + width + " height=" + height);
        long start = System.currentTimeMillis();
        Result rawResult = null;
        byte[] rotatedData = new byte[data.length];
        Log.d(TAG, "DecodeHandler decode rotatedData 前");
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                rotatedData[x * height + height - y - 1] = data[x + y * width];
            }
        }
        int tmp = width; // Here we are swapping, that's the difference to
        width = height;
        height = tmp;

        PlanarYUVLuminanceSource source = null;
        // boolean barcodeFlag = false;
        try {
            source = CameraManager.get().buildLuminanceSource(rotatedData,
                    width, height);

            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

            Log.d(TAG, "DecodeHandler decode (multiFormatReader.decodeWithState前");
            rawResult = multiFormatReader.decodeWithState(bitmap);
            Log.d(TAG, "DecodeHandler decode (multiFormatReader.decodeWithState后");

            if (rawResult.getBarcodeFormat() == BarcodeFormat.ANSWERCARD
                    && rawResult.getResultPoints().length > 3) {
                Rect r1 = CameraManager.get().getFramingRect();

                ResultPoint[] rps = rawResult.getResultPoints();

                Rect rect = CameraManager.get().getFramingRectInPreview();
                if (rect == null || rect.isEmpty()) {
                    multiFormatReader.reset();
                    return;
                }
                boolean pointIsRight = mAnswerCardCheckUtil.checkIsStraight(
                        rawResult.getResultPoints(), rect.width(),
                        rect.height());

                boolean distanceIsGood = mAnswerCardCheckUtil
                        .checkDistanceIsGood(rawResult.getResultPoints(),
                                rect.width(), rect.height());

                CWLog.e(TAG, "验证结果: pointIsRight = " + pointIsRight
                        + "  distanceIsGood =" + distanceIsGood);
                if (!pointIsRight || !distanceIsGood) {
                    Message fmessage = Message.obtain(
                            zingInterface.getHandler(), R.id.decode_failed);
                    fmessage.sendToTarget();

                    CWLog.e(TAG, "pointIsRight = " + pointIsRight
                            + " distanceIsGood = " + distanceIsGood
                            + " check point is not right !");
                    rawResult = null;
                    // barcodeFlag = true;
                }

                // rawResult.setResultPoints(getResultPointByPoint(points1));

            }
        } catch (Exception re) {
            Log.d(TAG, "DecodeHandler ReaderException " + re.getMessage());
            // continue
        } finally {
            multiFormatReader.reset();
            // if(barcodeFlag)
            // {
            // Message message = Message.obtain(zingInterface.getHandler(),
            // R.id.restart_preview);
            // message.sendToTarget();
            // return;
            // }
        }

        if (rawResult != null) {
            long end = System.currentTimeMillis();
            Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n"
                    + rawResult.toString());
            Message message = Message.obtain(zingInterface.getHandler(),
                    R.id.decode_succeeded, rawResult);
            Bundle bundle = new Bundle();
            if (source != null) {
                Bitmap bitmap = null;
                try {
                    bitmap = source.renderCroppedGreyscaleBitmap();
                    bundle.putParcelable(DecodeThread.BARCODE_BITMAP, bitmap);
                } catch (OutOfMemoryError e) {
                    e.printStackTrace();
                    if (bitmap != null && !bitmap.isRecycled()) {
                        bitmap.recycle();
                    }
                    Message failmessage = Message.obtain(zingInterface.getHandler(), R.id.decode_failed);
                    failmessage.sendToTarget();
                    return;
                }
            }
            message.setData(bundle);

            // Log.d(TAG, "Sending decode succeeded message...");
            message.sendToTarget();
        } else {
            Message message = Message.obtain(zingInterface.getHandler(), R.id.decode_failed);
            message.sendToTarget();
        }
    }

    public static Result scanningImage(Bitmap bitmap) {
        if (bitmap == null || bitmap.isRecycled()) {
            return null;
        }

        // DecodeHintType 和EncodeHintType
        // Hashtable<DecodeHintType, Object> hints = new
        // Hashtable<DecodeHintType, Object>();
        //
        // hints.put(DecodeHintType.CHARACTER_SET, "utf-8"); // 设置二维码内容的编码
        int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];
        bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0,
                bitmap.getWidth(), bitmap.getHeight());

        RGBLuminanceSource source = new RGBLuminanceSource(bitmap.getWidth(),
                bitmap.getHeight(), intArray);
        BinaryBitmap bitmap1 = new BinaryBitmap(new HybridBinarizer(source));


        Reader reader = new AnswerCardReader();
        reader.reset();
        Result result = null;
        try {
            result = reader.decode(bitmap1);
        } catch (NotFoundException e) {
            e.printStackTrace();
        } catch (ChecksumException e) {
            e.printStackTrace();
        } catch (FormatException e) {
            e.printStackTrace();
        } finally {
            if (bitmap != null && !bitmap.isRecycled()) {
                bitmap.recycle();
            }
        }
        return result;
    }

    public static Result scanningImage(String path) {
        Bitmap scanBitmap = null;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false; // 先获取原大小
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        scanBitmap = BitmapFactory.decodeFile(path.replace("file:///", ""),
                options);
        // options.inJustDecodeBounds = false; // 获取新的大小

        // int sampleSize = (int) (options.outHeight / (float) 200);
        //
        // if (sampleSize <= 0)
        // sampleSize = 1;
        return scanningImage(scanBitmap);
    }

    private Point getCardPoint(ResultPoint rp) {
        Rect rect = CameraManager.get().getFramingRectInPreview();
        int width = rect.left + rect.right;
        int oX = (int) (rp.getX() + rect.left);
        int oY = (int) (rp.getY() + rect.top);
        return new Point(oY, width - oX);
    }

    private void strethWidth(Point[] ps) {
        addOffset(ps[0], ps[1]);
        addOffset(ps[1], ps[2]);
        addOffset(ps[2], ps[3]);
        addOffset(ps[3], ps[0]);
    }

    private ResultPoint[] getResultPointByPoint(Point[] ps) {
        ResultPoint[] rps = new ResultPoint[4];
        int i = 0;
        for (Point p : ps) {
            rps[i++] = new ResultPoint(p.x, p.y);
        }
        return rps;
    }

    private void addOffset(Point p1, Point p2) {
        int offSet = dip2px(CameraManager.get().getContext(), 10);
        if (Math.abs(p1.x - p2.x) > Math.abs(p1.y - p2.y)) {
            if (p1.x > p2.x) {
                p1.x += offSet;
                p2.x -= offSet;
            } else {
                p1.x -= offSet;
                p2.x += offSet;
            }
        } else {
            if (p1.y > p2.y) {
                p1.y += offSet;
                p2.y -= offSet;
            } else {
                p1.y -= offSet;
                p2.y += offSet;
            }
        }
    }

    /**
     * ?????????* 将dip或dp值转换为px值，保证尺寸大小不变 ?????????*? ?????????* @param dipValue
     * ?????????* @param scale ?????????*??????????? （DisplayMetrics类中属性density）
     * ?????????* @return ?????????
     */
    public static int dip2px(Context context, int dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    // private static void bundleThumbnail(PlanarYUVLuminanceSource source,
    // Bundle bundle)
    // {
    // int[] pixels = source.renderThumbnail();
    // int width = source.getThumbnailWidth();
    // int height = source.getThumbnailHeight();
    // Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height,
    // Bitmap.Config.ARGB_8888);
    // ByteArrayOutputStream out = new ByteArrayOutputStream();
    // bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
    // bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());
    // bundle.putFloat(DecodeThread.BARCODE_SCALED_FACTOR, (float) width
    // / source.getWidth());
    // }
}
